package com.zh.work.aop;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.zh.common.entity.JSONResult;
import com.zh.common.entity.Oauth;
import com.zh.common.util.CheckObjAllFieldsIsNullUtil;
import com.zh.common.util.IpAddressUtil;
import com.zh.work.constant.ChannelConstants;
import com.zh.work.entity.ChannelRecordEntity;
import com.zh.work.entity.channel.*;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;

import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * code is far away from bug with the animal protecting
 * ┏┓　　　┏┓
 * ┏┛┻━━━┛┻┓
 * ┃　　　　　　　┃
 * ┃　　　━　　　┃
 * ┃　┳┛　┗┳　┃
 * ┃　　　　　　　┃
 * ┃　　　┻　　　┃
 * ┃　　　　　　　┃
 * ┗━┓　　　┏━┛
 * 　　┃　　　┃神兽保佑
 * 　　┃　　　┃代码无BUG！
 * 　　┃　　　┗━━━┓
 * 　　┃　　　　　　　┣┓
 * 　　┃　　　　　　　┏┛
 * 　　┗┓┓┏━┳┓┏┛
 * 　　　┃┫┫　┃┫┫
 * 　　　┗┻┛　┗┻┛
 *
 * @Description: 渠道aop记录
 * @Author: Li.HuiNeng
 * @Create in: 2019-12-18 14:26
 * @Version: 1.0.1
 */
//@Aspect
//@Component
public class ChannelRecordAop {
    private static final Logger log = LoggerFactory.getLogger(ChannelRecordAop.class);
    // 获取当前时间
    private String DATE = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());


    @Pointcut("execution(* com.zh.work.controller.platform.channel..putMod*(..)) " +
            "|| execution(* com.zh.work.controller.platform.channel..delete*(..))" )
    public void recordControllerLog() { }

    @Pointcut( "execution(* com.zh.work.controller.platform.channel..putAdd*(..))")
    public void addControllerLog() { }

    private static final String CHANNEL_QR_CODE_ENTITY = "ChannelQrCodeEntity";
    private static final String QR_CODE_CHANNEL_ROUTE_ENTITY = "QrCodeChannelRouteEntity";
    private static final String QR_CODE_MERCHANT_ENTITY = "QrCodeMerchEntity";
    private static final String ONLINE_CHANNEL_ENTITY = "OnlineChannelEntity";
    private static final String ONLINE_CHANNEL_MERCHANT_ENTITY = "OnlineChannelMerchEntity";
    private static final String ONLINE_ROUTE_ENTITY = "OnlineRouteEntity";
    private static final String QUICK_CHANNEL_ENTITY = "QuickChannelEntity";
    private static final String QUICK_MERCH_ENTITY = "QuickMerchEntity";
    private static final String QUICK_ROUTE_ENTITY = "QuickRouteEntity";
    private static final String PAY_CHANNEL_ENTITY = "PayChannelEntity";
    private static final String PAY_MERCH_ENTITY = "PayMerchEntity";
    private static final String PAY_ROUTE_ENTITY = "PayRouteEntity";



    /**
     * redis实例
     */
    @Autowired
    private RedisTemplate redisTemplate;

    @Qualifier("taskExecutor")
    @Autowired
    private TaskExecutor taskExecutor;

    @SuppressWarnings("all")
    @Async
    @Before(value = "recordControllerLog()")
    public void beforeLog(@NotNull JoinPoint jp) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        ChannelRecordEntity channelRecordEntity = new ChannelRecordEntity();
        // 获取ip
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        String ip = IpAddressUtil.getIpAddress(request);
        // 获取方法信息
        MethodSignature methodSignature = (MethodSignature) jp.getSignature();
        Method method = methodSignature.getMethod();
        // 获取当前操作人对象
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        Oauth auth = (Oauth) authentication.getPrincipal();
        /* 获取ApiOperation注解设置的notes描述内容作为信息 */
        ApiOperation apiOperation = null;
        if (method.isAnnotationPresent(ApiOperation.class)) {
            apiOperation = method.getAnnotation(ApiOperation.class);
        }
        JSONObject json;
        Long id = null;
        if (jp.getArgs() == null) {
            log.warn("JoinPoint中传入目标方法的参数对象为空!");
        }
        int i = 0;
        if (!(jp.getArgs()[0] instanceof ArrayList)) {
            json = CheckObjAllFieldsIsNullUtil.getReflect(jp.getArgs()[0]);
            Assert.notNull(json.get("Id"),"修改前置增强,主键为空!");
            id = Long.parseLong(json.get("Id").toString());
            // 获取类名，并选择对应的类查询，添加记录
            String className = jp.getArgs()[0].getClass().getSimpleName();
            // 获取不同实体类修改前值,存入缓存
            try {
                if (id != null && !"".equals(id.toString())) {
                    switch (className) {
                        case CHANNEL_QR_CODE_ENTITY:
                            ChannelQrCodeEntity codeEntity = new ChannelQrCodeEntity();
                            codeEntity = (ChannelQrCodeEntity) codeEntity.selectById(id);
                            if (codeEntity != null) {
                                i = 1;
                                this.redisTemplate.opsForValue().set(auth.getUserLoginName() + DATE, codeEntity, 60, TimeUnit.DAYS);
                            }
                            assert codeEntity != null;
                            channelRecordEntity.setBeforeValue(JSON.toJSONString(codeEntity));
                            break;
                        case QR_CODE_CHANNEL_ROUTE_ENTITY:
                            QrCodeChannelRouteEntity routeEntity = new QrCodeChannelRouteEntity();
                            routeEntity = (QrCodeChannelRouteEntity) routeEntity.selectById(id);
                            if (routeEntity != null) {
                                i = 1;
                                this.redisTemplate.opsForValue().set(auth.getUserLoginName() + DATE, routeEntity, 60, TimeUnit.DAYS);
                            }
                            assert routeEntity != null;
                            channelRecordEntity.setBeforeValue(JSON.toJSONString(routeEntity));
                            break;
                        case QR_CODE_MERCHANT_ENTITY:
                            QrCodeMerchEntity merchEntity = new QrCodeMerchEntity();
                            merchEntity = (QrCodeMerchEntity) merchEntity.selectById(id);
                            if (merchEntity != null) {
                                i = 1;
                                this.redisTemplate.opsForValue().set(auth.getUserLoginName() + DATE, merchEntity, 60, TimeUnit.DAYS);
                            }
                            assert merchEntity != null;
                            channelRecordEntity.setBeforeValue(JSON.toJSONString(merchEntity));
                            break;
                        case ONLINE_CHANNEL_ENTITY:
                            // 对网关渠道数据变更时,清除网关渠道数据缓存
                            this.redisTemplate.delete(ChannelConstants.ONLINE_CHANNEL_CACHE);
                            OnlineChannelEntity onlineChannelEntity = new OnlineChannelEntity();
                            onlineChannelEntity = (OnlineChannelEntity) onlineChannelEntity.selectById(id);
                            if (onlineChannelEntity != null) {
                                i = 1;
                                this.redisTemplate.opsForValue().set(auth.getUserLoginName() + DATE, onlineChannelEntity, 60, TimeUnit.DAYS);
                            }
                            assert onlineChannelEntity != null;
                            channelRecordEntity.setBeforeValue(JSON.toJSONString(onlineChannelEntity));
                            break;
                        case ONLINE_CHANNEL_MERCHANT_ENTITY:
                            OnlineChannelMerchEntity channelEntity = new OnlineChannelMerchEntity();
                            channelEntity = (OnlineChannelMerchEntity) channelEntity.selectById(id);
                            if (channelEntity != null) {
                                i = 1;
                                this.redisTemplate.opsForValue().set(auth.getUserLoginName() + DATE, channelEntity, 60, TimeUnit.DAYS);
                            }
                            assert channelEntity != null;
                            channelRecordEntity.setBeforeValue(JSON.toJSONString(channelEntity));
                            break;
                        case ONLINE_ROUTE_ENTITY:
                            OnlineRouteEntity onlineRouteEntity = new OnlineRouteEntity();
                            onlineRouteEntity = (OnlineRouteEntity) onlineRouteEntity.selectById(id);
                            if (onlineRouteEntity != null) {
                                i = 1;
                                this.redisTemplate.opsForValue().set(auth.getUserLoginName() + DATE, onlineRouteEntity, 60, TimeUnit.DAYS);
                            }
                            assert onlineRouteEntity != null;
                            channelRecordEntity.setBeforeValue(JSON.toJSONString(onlineRouteEntity));
                            break;
                        case QUICK_CHANNEL_ENTITY:
                            // 对快捷渠道数据变更时,清除快捷渠道数据缓存
                            this.redisTemplate.delete(ChannelConstants.QUICK_CHANNEL_CACHE);
                            QuickChannelEntity quickChannelEntity = new QuickChannelEntity();
                            quickChannelEntity = (QuickChannelEntity) quickChannelEntity.selectById(id);
                            if (quickChannelEntity != null) {
                                i = 1;
                                this.redisTemplate.opsForValue().set(auth.getUserLoginName() + DATE, quickChannelEntity, 60, TimeUnit.DAYS);
                            }
                            assert quickChannelEntity != null;
                            channelRecordEntity.setBeforeValue(JSON.toJSONString(quickChannelEntity));
                            break;
                        case QUICK_MERCH_ENTITY:
                            QuickMerchEntity quickMerchEntity = new QuickMerchEntity();
                            quickMerchEntity = (QuickMerchEntity) quickMerchEntity.selectById(id);
                            if (quickMerchEntity != null) {
                                i = 1;
                                this.redisTemplate.opsForValue().set(auth.getUserLoginName() + DATE, quickMerchEntity, 60, TimeUnit.DAYS);
                            }
                            assert quickMerchEntity != null;
                            channelRecordEntity.setBeforeValue(JSON.toJSONString(quickMerchEntity));
                            break;
                        case QUICK_ROUTE_ENTITY:
                            QuickRouteEntity quickRouteEntity = new QuickRouteEntity();
                            quickRouteEntity = (QuickRouteEntity) quickRouteEntity.selectById(id);
                            if (quickRouteEntity != null) {
                                i = 1;
                                this.redisTemplate.opsForValue().set(auth.getUserLoginName() + DATE, quickRouteEntity, 60, TimeUnit.DAYS);
                            }
                            assert quickRouteEntity != null;
                            channelRecordEntity.setBeforeValue(JSON.toJSONString(quickRouteEntity));
                            break;
                        case PAY_CHANNEL_ENTITY:
                            // 对代付渠道数据变更时,清除代付渠道数据缓存
                            this.redisTemplate.delete(ChannelConstants.PAY_CHANNEL_CACHE);
                            PayChannelEntity payChannelEntity = new PayChannelEntity();
                            payChannelEntity = (PayChannelEntity) payChannelEntity.selectById(id);
                            if (payChannelEntity != null) {
                                i = 1;
                                this.redisTemplate.opsForValue().set(auth.getUserLoginName() + DATE, payChannelEntity, 60, TimeUnit.DAYS);
                            }
                            assert payChannelEntity != null;
                            channelRecordEntity.setBeforeValue(JSON.toJSONString(payChannelEntity));
                            break;
                        case PAY_MERCH_ENTITY:
                            PayMerchEntity payMerchEntity = new PayMerchEntity();
                            payMerchEntity = (PayMerchEntity) payMerchEntity.selectById(id);
                            if (payMerchEntity != null) {
                                i = 1;
                                this.redisTemplate.opsForValue().set(auth.getUserLoginName() + DATE, payMerchEntity, 60, TimeUnit.DAYS);
                            }
                            assert payMerchEntity != null;
                            channelRecordEntity.setBeforeValue(JSON.toJSONString(payMerchEntity));
                            break;
                        case PAY_ROUTE_ENTITY:
                            PayRouteEntity payRouteEntity = new PayRouteEntity();
                            payRouteEntity = (PayRouteEntity) payRouteEntity.selectById(id);
                            if (payRouteEntity != null) {
                                i = 1;
                                this.redisTemplate.opsForValue().set(auth.getUserLoginName() + DATE, payRouteEntity, 60, TimeUnit.DAYS);
                            }
                            assert payRouteEntity != null;
                            channelRecordEntity.setBeforeValue(JSON.toJSONString(payRouteEntity));
                            break;
                        default:
                            log.warn("未找到对应数据类型,修改前数据未存入缓存,请尽快检查问题。");
                    }
                    channelRecordEntity.setAfterValue(json.toJSONString());
                    channelRecordEntity.setRequestParam(json.toJSONString());
                } else {
                    // 用于记录最终修改值
                    StringBuffer sb = new StringBuffer();
                    ArrayList arrayList = (ArrayList) jp.getArgs()[0];
                    arrayList.forEach(data -> {
                        sb.append(JSON.toJSONString(data) + "#");
                    });
                    sb.substring(0, sb.length() - 1);
                    channelRecordEntity.setRequestParam(sb.toString());
                }
                taskExecutor.execute(() -> {
                    channelRecordEntity.setRequestFunc(jp.getTarget().getClass().getName() + method.getName());
                    channelRecordEntity.setCreateTime(DATE);
                    channelRecordEntity.setIp(ip);
                    channelRecordEntity.setOperatorId(auth.getId());
                    channelRecordEntity.setSuccess(true);
                    try {
                        channelRecordEntity.insert();
                    } catch (Exception e) {
                        log.error("插入历史记录失败,错误原因:{}", e.getLocalizedMessage());
                    }
                });
                assert apiOperation != null;
                log.info("请求记录:{}.{}==>[ {} ]", jp.getTarget().getClass().getName(), method.getName(), apiOperation.notes());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    @SuppressWarnings("all")
    @Async
    @AfterReturning(value = "addControllerLog()", returning = "result")
    public void addAfterLog(@NotNull JoinPoint jp, JSONResult result) {
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        String ip = IpAddressUtil.getIpAddress(request);
        // 获取当前操作人对象
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        Oauth auth = (Oauth) authentication.getPrincipal();
        // 排除为空参数之后保留的参数
        JSONObject json = new JSONObject();
        try {
            json = CheckObjAllFieldsIsNullUtil.getReflect(jp.getArgs()[0]);
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        ChannelRecordEntity recordEntity = new ChannelRecordEntity();
        MethodSignature methodSignature = (MethodSignature) jp.getSignature();
        Method method = methodSignature.getMethod();
        try {
            //获取ApiOperation注解设置的notes描述内容作为信息
            if (method.isAnnotationPresent(ApiOperation.class)) {
                ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
                //方法完整路径,方法名,备注信息
                log.info("请求记录:{}.{}==>[ {} ]", jp.getTarget().getClass().getName(), method.getName(), apiOperation.notes());
                recordEntity.setRequestFunc(jp.getTarget().getClass().getName() + method.getName());
                recordEntity.setCreateTime(DATE);
                recordEntity.setIp(ip);
                recordEntity.setOperatorId(auth.getId());
                recordEntity.setRequestParam(json.toJSONString());
                recordEntity.setSuccess(result.getSuccess());
                recordEntity.setRemark(StringUtils.isNotEmpty(result.getMessage()) ? result.getMessage(): "");
                try {
                    recordEntity.insert();
                } catch (Exception e) {
                    log.error("插入历史记录失败,错误原因:{}", e.getLocalizedMessage());
                }
            }
            // 将当前操作人登录名+当前时间(时分秒)作为key存入redis中,60天失效期
            redisTemplate.opsForValue().set(auth.getUserLoginName() + DATE, json, 60, TimeUnit.DAYS);
        } catch (Exception e) {
            log.error("异常信息:{}", e.getLocalizedMessage());
        }
    }
}
